package com.max.common.oauth.oauth10;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Random;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

import com.max.common.util.BASE64Encoder;
import com.max.common.util.HttpTool.Response;

public abstract class OAuthApiBase implements OAuthApi
{
	
	protected OAuthConsumer consumer = null;
	
	private final Random RAND = new Random();
	
	private final String HMAC_SHA1 = "HmacSHA1";
	
	protected String callback = null;
	
	private String oauth_verifier = null;
	
	
	public OAuthApiBase(String consumer)
	{
		this.consumer = Config.getInstance().getConsumerMap().get(consumer);
	}
	
	
	@Override
	public OAuthToken requestToken() throws Exception
	{
		OAuthToken requestToken = null;
		
		String method = consumer.getProvider().getMethod().equalsIgnoreCase("POST")?"POST":"GET";
		
		List<OAuthParameter> paramList  = new ArrayList<OAuthParameter>();
		paramList.add(new OAuthParameter("oauth_callback", callback));
		
		Response res = request(method,consumer.getProvider().getRequestTokenURL(), paramList,null);
		Map<String, String> paramMap = res.asParamMap();
		requestToken = new OAuthToken(paramMap.get("oauth_token"),paramMap.get("oauth_token_secret"));
		
		return requestToken;
	}
	
	
	@Override
	public void authorization(OAuthToken token) throws Exception
	{
		// TODO Auto-generated method stub
	}
	
	
	@Override
	public OAuthToken accessToken(OAuthToken requestToken) throws Exception
	{
		OAuthToken accessToken = null;
		
		if(oauth_verifier == null || oauth_verifier.trim().equals(""))
			throw new OAuthException(1,"oauth_verifier string error!");
		
		String method = consumer.getProvider().getMethod().equalsIgnoreCase("POST")?"POST":"GET";
		
		List<OAuthParameter> postParamList  = new ArrayList<OAuthParameter>();
		postParamList.add(new OAuthParameter("oauth_verifier", oauth_verifier));
		
		Response res = request(method,consumer.getProvider().getAccessTokenURL(), postParamList, requestToken);
		Map<String, String> paramMap = res.asParamMap();
		accessToken = new OAuthToken(paramMap.get("oauth_token"),paramMap.get("oauth_token_secret"));
		
		return accessToken;
	}
	
	public abstract Response request(String method,String url, List<OAuthParameter> paramList, OAuthToken token);
	
	public List<OAuthParameter> generateOAuthParams(OAuthToken token)
	{
		List<OAuthParameter> oauthParamList = new ArrayList<OAuthParameter>();
		
		oauthParamList.add(new OAuthParameter("oauth_consumer_key", consumer.getConsumer_key()));
		oauthParamList.add(new OAuthParameter("oauth_signature_method", "HMAC-SHA1"));
		oauthParamList.add(new OAuthParameter("oauth_timestamp", System.currentTimeMillis()/1000 + ""));
		oauthParamList.add(new OAuthParameter("oauth_nonce", System.currentTimeMillis()/1000 + RAND.nextInt() + ""));
		oauthParamList.add(new OAuthParameter("oauth_version", "1.0"));

		if(token != null)
			oauthParamList.add(new OAuthParameter("oauth_token", token.getOauth_token()));
		
//		LogUtil.debug("oauthParamList:",oauthParamList);
		return oauthParamList;
	}
	
	
	public static String encode(String value) 
	{
		if(value == null)
			return value;
		
        String encoded = null;
        try 
        {
            encoded = URLEncoder.encode(value, "UTF-8");
        } catch 
        (UnsupportedEncodingException ignore) 
        {
        }
        StringBuffer buf = new StringBuffer(encoded.length());
        char focus;
        for (int i = 0; i < encoded.length(); i++) 
        {
            focus = encoded.charAt(i);
            if (focus == '*') 
            {
                buf.append("%2A");
            } 
            else if (focus == '+') 
            {
                buf.append("%20");
            } 
            else if (focus == '%' && (i + 1) < encoded.length()
                    && encoded.charAt(i + 1) == '7' && encoded.charAt(i + 2) == 'E') 
            {
                buf.append('~');
                i += 2;
            } 
            else 
            {
                buf.append(focus);
            }
        }
        
        return buf.toString();
    }
	
	
	public static String encodeParameters(List<OAuthParameter> params) 
	{
        return encodeParameters(params, "&", false);
    }
	
	
	public static String encodeParameters(List<OAuthParameter> params,String splitter, boolean quot) 
	{
//        StringBuffer buf = new StringBuffer();
//        
//        for (OAuthParameter param : params) 
//        {
//            if (buf.length() != 0) 
//            	buf.append("splitter");
//            
//            buf.append(encode(param.getName())).append("=").append(encode(param.getValue()));
//        }
//        
//        return buf.toString();
		
		StringBuffer buf = new StringBuffer();
		
        for (OAuthParameter param : params) 
        {
            if (buf.length() != 0) 
            {
                if (quot) 
                	buf.append("\"");
                buf.append(splitter);
            }
            
            buf.append(encode(param.getName())).append("=");
            
            if (quot) 
            	buf.append("\"");
            buf.append(encode(param.getValue()));
        }
        
        if (buf.length() != 0)
        {
            if (quot)
            	buf.append("\"");
        }
        
        return buf.toString();
    }
	
	public static String constructRequestURL(String url)
	{
        int index = url.indexOf("?");
        if (-1 != index)
        	url = url.substring(0, index);
        
        int slashIndex = url.indexOf("/", 8);
        String baseURL = url.substring(0, slashIndex).toLowerCase();
        int colonIndex = baseURL.indexOf(":", 8);
        
        if (-1 != colonIndex) 
        {
            if (baseURL.startsWith("http://") && baseURL.endsWith(":80"))
            	baseURL = baseURL.substring(0, colonIndex);
            else if (baseURL.startsWith("https://") && baseURL.endsWith(":443"))
            	baseURL = baseURL.substring(0, colonIndex);
        }
        
        url = baseURL + url.substring(slashIndex);

        return url;
    }
	
	
	public String signParameters(String method,String url,List<OAuthParameter> params,OAuthToken token)
	{
		Collections.sort(params);
		StringBuffer base = new StringBuffer(encode(method.equalsIgnoreCase("post")?"POST":"GET"))
									.append("&")
									.append(encode(constructRequestURL(url)))
									.append("&")
									.append(encode(encodeParameters(params)));
		
		String oauthBaseString = base.toString();
		
		String signature = generateSignature(oauthBaseString, token);
		
		return signature;
	}
	
	
	public String generateSignature(String data, OAuthToken token) 
	{
        byte[] byteHMAC = null;
        
        try 
        {
            Mac mac = Mac.getInstance(HMAC_SHA1);
            SecretKeySpec spec = null;
            String oauthSignature = encode(consumer.getConsumer_secret()) + "&" + (token==null?"":encode(token.getOauth_token_secret()));
            
            spec = new SecretKeySpec(oauthSignature.getBytes(), HMAC_SHA1);
            
            mac.init(spec);
            byteHMAC = mac.doFinal(data.getBytes());
        } 
        catch (Exception e) 
        {
            e.printStackTrace();
        }
        
        return new BASE64Encoder().encode(byteHMAC);
    }

	public String getCallback() 
	{
		return callback;
	}


	public void setCallback(String callback) 
	{
		this.callback = callback;
	}


	public String getOauth_verifier() 
	{
		return oauth_verifier;
	}


	public void setOauth_verifier(String oauth_verifier) 
	{
		this.oauth_verifier = oauth_verifier;
	}


	public OAuthConsumer getConsumer() 
	{
		return consumer;
	}
}
